use common_base::prelude::*;

use crate::{
    data::{DataObject, ParamObject, ResponseObject},
    db_manager::DbConnManager,
};

use super::{service_builder::ServiceBuilder, service_executor_manager::ServiceExecutorManager};

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum ServiceComponentRunType {
    Sync,
    Async,
}

pub struct ServiceComponentManager;

impl ServiceComponentManager {
    pub fn initialize(service_container_dir: &str, service_dylib_dir: &str) {
        ServiceBuilder::initialize(service_container_dir, service_dylib_dir)
    }

    pub async fn sync_run_service(
        mut param_object: ParamObject,
        data_object: Option<DataObject>, /* custom_object, addon_object */
    ) -> Result<ResponseObject> {
        let mut service_key = param_object.get_value_by_param_name("ServiceKey");
        if service_key.is_empty() {
            service_key = param_object.get_value_by_env_name("ServiceKey");
        }

        let service_keys = service_key.split(",").collect::<Vec<&str>>();
        if service_keys.len() == 1 {
            param_object.set_value_by_env_name("ServiceKey", service_keys[0].to_owned());
            Self::sync_run_service_with_no_connection(
                param_object,
                data_object, /* custom_object, addon_object */
            )
            .await
        } else {
            Err(ErrorCode::ServiceKeyInvalid(format!(
                "can not service for invalid service key {}",
                service_key,
            )))
        }
    }

    async fn sync_run_service_with_no_connection(
        mut param_object: ParamObject,
        data_object: Option<DataObject>,
        /* custom_object, addon_object */
    ) -> Result<ResponseObject> {
        let app_desktop = ServiceBuilder::get_service_app_desktop(&mut param_object).await?;

        let connection = if app_desktop.value().service_container.need_db_connection {
            // TODO: should we use iom to get dbConn? what are advantages?
            // Some(DbConnection::get_instance(param_object).await?)
            Some(DbConnManager::get_connection(&mut param_object).await?)
        } else {
            None
        };

        let response_object = ServiceExecutorManager::sync_run_service(
            app_desktop.value().service_container.clone(),
            param_object,
            data_object,
            connection,
            ResponseObject::default(),
        )
        .await
        .map_err(|e| {
            warn!("service sync run faield, cause: {}.", e);
            e
        });

        // we do not need close connection, because the connection is Send to a Async Future, when that Future is finished, the connection will be Drop automaticly.

        response_object
    }
}
